//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_AUTO_TRAITS_H_
#define _BUOLA_AUTO_TRAITS_H_

#include <type_traits>
#include <memory>
#include <limits>

namespace buola {

///\addtogroup traits
///@{

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class OR
///
/// Given two trait result classes, creates a new class that is the logical OR of the other two
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename t1,typename t2>
using QOr=std::integral_constant<bool,t1::value||t2::value>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class AND
///
/// Given two trait result classes, creates a new class that is the logical AND of the other two
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename t1,typename t2>
using QAnd=std::integral_constant<bool,t1::value&&t2::value>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class NOT
///
/// Given a trait result class, creates a new class that is the logical NOT of the other one
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using QNot=std::integral_constant<bool,!tT::value>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// boolean integral constant
///
/// To create an integral constant type of type bool and the given value
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<bool vB>
using QBool=std::integral_constant<bool,vB>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class to query if a type is a reference type
///
/// It returns true if it is either an lvalue or an rvalue reference
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using QIsReference=QOr<std::is_lvalue_reference<tT>,std::is_rvalue_reference<tT>>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class to query if a type is a const type
///
/// It returns true if it is a const-qualified type
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using QIsConst=std::is_const<tT>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class to query if a type is a numeric type
///
/// It returns true if it is an integral or floating point number
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using QIsNumeric=QBool<std::numeric_limits<tT>::is_specialized>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// trait class to query if a type is a POD
///
/// It returns true if it is a POD
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using QIsPOD=std::is_pod<tT>;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// decayed version of \p tT
///
/// uses std::decay to return a type which is the base type with all reference and cv-qualifiers removed
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using ODecay=typename std::decay<tT>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// remove reference from type
///
/// uses std::remove_reference to return a type which is the base type without reference qualifiers
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using ORemoveRef=typename std::remove_reference<tT>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// remove CV-qualifiers from type
///
/// uses std::remove_reference to return a type which is the base type without cv-qualifiers
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tT>
using ORemoveCV=typename std::remove_cv<tT>::type;

namespace detail {

    template<typename... tAll>
    struct MLastHelper;

    template<typename tLast>
    struct MLastHelper<tLast>
    {
        typedef tLast type;
    };

    template<typename tFirst,typename... tRest>
    struct MLastHelper<tFirst,tRest...>
    {
        typedef typename MLastHelper<tRest...>::type type;
    };
    
    template<typename tFirst,typename... tRest>
    struct MFirstHelper
    {
        typedef tFirst type;
    };
        
/*namespace detail*/ }

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// return the first type in list
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename... tAll>
using OFirst=typename detail::MFirstHelper<tAll...>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// return the last type in list
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename... tAll>
using OLast=typename detail::MLastHelper<tAll...>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// typedef version of std::enable_if
///
/// Main difference with std::enable_if is that the first parameter, instead of being a boolean value
/// is a traits type with a \p value boolean member.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tBoolean,typename tResult=int>
using OEnableIf=typename std::enable_if<tBoolean::value,tResult>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag version of std::enable_if
///
/// It enables the template (and returns the last template parameter) if \p tType inherits from \p tTag.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType,typename tTag,typename tResult=int>
using OEnableIfTag=typename std::enable_if<std::is_base_of<tTag,ODecay<tType>>::value,tResult>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag version of std::enable_if (2 types)
///
/// It enables the template (and returns the last template parameter) if \p tType inherits from \p tTag.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType1,typename tType2,typename tTag,typename tResult=int>
using OEnableIfTag2=typename std::enable_if<std::is_base_of<tTag,ODecay<tType1>>::value&&
                                            std::is_base_of<tTag,ODecay<tType2>>::value,tResult>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// typedef negated version of std::enable_if
///
/// Main difference with std::enable_if is that the first parameter, instead of being a boolean value
/// is a traits type with a \p value boolean member.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tBoolean,typename tResult=int>
using OEnableIfNot=typename std::enable_if<!tBoolean::value,tResult>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// returns the type which is common to all argument types
///
/// That is, the type to which they can all be converted.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename... tTypes>
using OCommon=typename std::common_type<tTypes...>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// typedef version of std::conditional
///
/// The first parameter is a traits type instead of a boolean value.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tBoolean,typename tTrue,typename tFalse>
using OConditional=typename std::conditional<tBoolean::value,tTrue,tFalse>::type;

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// obtain a similar pointer to type \p tNew
///
/// The pointer will be of the same nature as \p tPtr.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tPtr,typename tNew>
using ORebindPointer=typename std::pointer_traits<tPtr>::template rebind<tNew>;

template<template<typename> class tPredicate,typename... tArgs>
struct QAll;

template<template<typename> class tPredicate>
struct QAll<tPredicate> : public std::true_type {};

template<template<typename> class tPredicate,typename tFirst,typename... tRest>
struct QAll<tPredicate,tFirst,tRest...> : public std::integral_constant<bool,(tPredicate<tFirst>::value&&
                                                                            QAll<tPredicate,tRest...>::value)> 
{};

template<template<typename> class tPredicate,typename... tArgs>
struct QAny;

template<template<typename> class tPredicate>
struct QAny<tPredicate> : public std::false_type {};

template<template<typename> class tPredicate,typename tFirst,typename... tRest>
struct QAny<tPredicate,tFirst,tRest...> : public std::integral_constant<bool,(tPredicate<tFirst>::value||
                                                                            QAny<tPredicate,tRest...>::value)> 
{};

///@}

/*namespace buola*/ }

#endif
